﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Assignment_2
{
    class TheCloudSphere : GameObject
    {
        #region Variables
        private VertexPositionNormalTexture[] vertices;
        private short[] indices;
        private VertexBuffer meshVB;
        private IndexBuffer meshIB;
        private int slices, stacks, count;
        private float radius;
        private GraphicsDevice graphicsDevice;

        public float scale{ get; set;}        
        #endregion

        #region Constants
        private const int MAPSIZE = 100;
        private const float RADIUS = 1;
        #endregion

        public TheCloudSphere(GraphicsDevice graphicsDevice, int slices, int stacks, float radius, Matrix realWorld) : base(graphicsDevice, realWorld)
        {            
            this.graphicsDevice = graphicsDevice;
            this.slices = slices;
            this.stacks = stacks;
            this.radius = radius;

            InitializeVertices();
            InitializeIndices();

            meshVB = new VertexBuffer(graphicsDevice, typeof(VertexPositionNormalTexture), vertices.Length, BufferUsage.WriteOnly);
            meshIB = new IndexBuffer(graphicsDevice, typeof(short), indices.Length, BufferUsage.WriteOnly);

            meshVB.SetData<VertexPositionNormalTexture>(vertices);
            meshIB.SetData<short>(indices);
        }

        /// <summary>
        /// Initializes the mesh vertices.
        /// </summary>
        public override void InitializeVertices()
        {
            vertices = new VertexPositionNormalTexture[(slices + 1) * (stacks + 1)];

            float phi, theta;
            float dphi = MathHelper.Pi / stacks;
            float dtheta = MathHelper.TwoPi / slices;
            float x, y, z, sc;
            int index = 0;

            for (int stack = 0; stack <= stacks; stack++)
            {
                phi = MathHelper.PiOver2 - stack * dphi;
                y = radius * (float)Math.Sin(phi);
                sc = -radius * (float)Math.Cos(phi);

                for (int slice = 0; slice <= slices; slice++)
                {
                    theta = slice * dtheta;
                    x = sc * (float)Math.Sin(theta);
                    z = sc * (float)Math.Cos(theta);
                    vertices[index++] = new VertexPositionNormalTexture(new Vector3(x, y, z),
                                                                    new Vector3(x, y, z),
                                                                    new Vector2((float)slice / (float)slices, (float)stack / (float)stacks));
                }
            }            
        }

        /// <summary>
        /// Initializes the mesh indices.
        /// </summary>
        public override void InitializeIndices()
        {
            indices = new short[slices * stacks * 6];
            int index = 0;
            int k = slices + 1;
            bool flippedTile = false;

            for (int stack = 0; stack < stacks; stack++)
            {
                for (int slice = 0; slice < slices; slice++)
                {
                    if (!flippedTile)
                    {
                        indices[index++] = (short)((stack + 0) * k + slice);
                        indices[index++] = (short)((stack + 1) * k + slice);
                        indices[index++] = (short)((stack + 0) * k + slice + 1);

                        indices[index++] = (short)((stack + 0) * k + slice + 1);
                        indices[index++] = (short)((stack + 1) * k + slice);
                        indices[index++] = (short)((stack + 1) * k + slice + 1);
                        flippedTile = !flippedTile;
                    }
                    else {
                        indices[index++] = (short)((stack + 0) * k + slice);
                        indices[index++] = (short)((stack + 1) * k + slice + 1);
                        indices[index++] = (short)((stack + 0) * k + slice + 1);

                        indices[index++] = (short)((stack + 0) * k + slice);
                        indices[index++] = (short)((stack + 1) * k + slice);
                        indices[index++] = (short)((stack + 1) * k + slice + 1);
                        flippedTile = !flippedTile;
                    }                  
                }
                flippedTile = !flippedTile;
            }            
        }

        /// <summary>
        /// Builds the mesh.
        /// </summary>
        /// <param name="tLeft"></param>
        /// <param name="tRight"></param>
        /// <param name="bLeft"></param>
        /// <param name="bRight"></param>
        private void buildMesh(int tLeft, int tRight, int bLeft, int bRight)
        {
            indices[count++] = (short)tLeft;
            indices[count++] = (short)bLeft;
            indices[count++] = (short)bRight;

            indices[count++] = (short)tLeft;
            indices[count++] = (short)bRight;
            indices[count++] = (short)tRight;
        }

        /// <summary>
        /// This is called when the water should draw itself.
        /// </summary>
        /// <param name="effect">The effect to use.</param>
        /// <param name="world">The world matrix.</param>
        public override void Draw(BasicEffect effect, Matrix world) 
        {           
            foreach (GameObject gameObject in children)
            {
                gameObject.Draw(effect, objWorld);
            }

            effect.World = objWorld * Matrix.CreateScale(1.02f);
            effect.EnableDefaultLighting();
            effect.CurrentTechnique.Passes[0].Apply();
            graphicsDevice.SetVertexBuffer(meshVB);
            graphicsDevice.Indices = meshIB;
            graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Length, 0, indices.Length / 3);
        }

        /// <summary>
        /// Allows the water to run logic.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="worldMatrix"></param>
        public override void Update(GameTime gameTime, Matrix worldMatrix)
        {
            foreach (GameObject child in children)
            {
                child.Update(gameTime, worldMatrix);
            }

            objWorld = Matrix.CreateFromQuaternion(this._rotation) * worldMatrix;
        }

        /// <summary>
        /// Allows the water sphere to rotate with a certain speed
        /// and in a certain direction.
        /// </summary>
        /// <param name="rotationSpeed">The rotational speed.</param>
        /// <param name="dir">The direction.</param>
        public void Rotate(float rotationSpeed, Constants.Direction dir)
        {
            if (dir == Constants.Direction.Forward)
                this._rotation = Quaternion.Concatenate(this._rotation, Quaternion.CreateFromYawPitchRoll(0, rotationSpeed, 0));            
            else if (dir == Constants.Direction.Backward)
                this._rotation = Quaternion.Concatenate(this._rotation, Quaternion.CreateFromYawPitchRoll(0, -rotationSpeed, 0));
            else if (dir == Constants.Direction.Left)
                this._rotation = Quaternion.Concatenate(this._rotation, Quaternion.CreateFromYawPitchRoll(-rotationSpeed, 0, 0));
            else if (dir == Constants.Direction.Right)
                this._rotation = Quaternion.Concatenate(this._rotation, Quaternion.CreateFromYawPitchRoll(rotationSpeed, 0, 0));
        }
    }
}